#include "slog.h"

#if !SLOG_SIMPLIFICATION

#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#if (SLOG_USE_LOG_COLOR)
#define LOG_NONE         "\033[m"
#define LOG_RED          "\033[0;32;31m"
#define LOG_LIGHT_RED    "\033[1;31m"
#define LOG_GREEN        "\033[0;32;32m"
#define LOG_LIGHT_GREEN  "\033[1;32m"
#define LOG_BLUE         "\033[0;32;34m"
#define LOG_LIGHT_BLUE   "\033[1;34m"
#define LOG_DARY_GRAY    "\033[1;30m"
#define LOG_CYAN         "\033[0;36m"
#define LOG_LIGHT_CYAN   "\033[1;36m"
#define LOG_PURPLE       "\033[0;35m"
#define LOG_LIGHT_PURPLE "\033[1;35m"
#define LOG_BROWN        "\033[0;33m"
#define LOG_YELLOW       "\033[1;33m"
#define LOG_LIGHT_GRAY   "\033[0;37m"
#define LOG_WHITE        "\033[1;37m"
#else
#define LOG_NONE         ""
#define LOG_RED          ""
#define LOG_LIGHT_RED    ""
#define LOG_GREEN        ""
#define LOG_LIGHT_GREEN  ""
#define LOG_BLUE         ""
#define LOG_LIGHT_BLUE   ""
#define LOG_DARY_GRAY    ""
#define LOG_CYAN         ""
#define LOG_LIGHT_CYAN   ""
#define LOG_PURPLE       ""
#define LOG_LIGHT_PURPLE ""
#define LOG_BROWN        ""
#define LOG_YELLOW       ""
#define LOG_LIGHT_GRAY   ""
#define LOG_WHITE        ""
#endif

// 定义unix和winndows的回车区别

#define UNIX

#ifdef UNIX
#define LOG_CRLF "\n"
#else
#define LOG_CRLF "\r\n"
#endif

static circular_queue_t log_queue;
static struct slog      log_obj;

#if SLOG_USE_ASYNC && SLOG_USE_PTHREAD_COND
static pthread_cond_t log_cond   = PTHREAD_COND_INITIALIZER;
static pthread_cond_t space_cond = PTHREAD_COND_INITIALIZER; // 增加条件变量用于队列空间通知
#endif

// 判断某个数组的开头有几个连续的\n
static inline int is_new_line_for_head(const char *arr, const size_t size)
{
    if (arr == NULL)
        return 0; // 如果 arr 为 NULL，返回 0
    int i;
    for (i = 0; i < size; i++)
    {
        if (arr[i] != '\n')
            break;
    }
    return i;
}

/**
 * @brief 日志输出格式
 * 需要将record->content开头的所有连续的\n输出到日志的最开头，而不是最后
 *
 * @param record
 * @param current_time
 */
static void _slog_out(slog_record *record, char *current_time)
{
    uint32_t offset = is_new_line_for_head(record->content, strlen(record->content));

    // 输出开头的换行符
    for (uint32_t i = 0; i < offset; i++)
    {
        log_obj.ops.out(LOG_CRLF);
    }

    if (record->is_hexdump)
    {
        switch (record->level)
        {
        case _LOG_LEVEL_FATAL  : log_obj.ops.out(LOG_CRLF LOG_LIGHT_RED "[F] %s %s" LOG_NONE LOG_CRLF, current_time, record->content + offset); break;
        case _LOG_LEVEL_ERROR  : log_obj.ops.out(LOG_CRLF LOG_LIGHT_RED "[E] %s %s" LOG_NONE LOG_CRLF, current_time, record->content + offset); break;
        case _LOG_LEVEL_WARNING: log_obj.ops.out(LOG_CRLF LOG_YELLOW "[W] " LOG_NONE "%s %s" LOG_CRLF, current_time, record->content + offset); break;
        case _LOG_LEVEL_NOTICE : log_obj.ops.out(LOG_BLUE "[N] %s %s" LOG_NONE LOG_CRLF, current_time, record->content + offset); break;
        case _LOG_LEVEL_INFO   : log_obj.ops.out(LOG_GREEN "[I] " LOG_NONE "%s %s" LOG_CRLF, current_time, record->content + offset); break;
        case _LOG_LEVEL_DEBUG  : log_obj.ops.out(LOG_DARY_GRAY "[D] " LOG_NONE "%s %s" LOG_CRLF, current_time, record->content + offset); break;
        case _LOG_LEVEL_NOISY  : log_obj.ops.out(LOG_DARY_GRAY "[ ] " LOG_NONE "%s %s" LOG_CRLF, current_time, record->content + offset); break;

        default: break;
        }
    }
    else
    {
        switch (record->level)
        {
        case _LOG_LEVEL_FATAL:
            log_obj.ops.out(LOG_CRLF LOG_LIGHT_RED "[F] %s %s %s:%d\t%s" LOG_NONE LOG_CRLF LOG_CRLF, current_time, record->file_name, record->func_name,
                            record->line, record->content + offset);
            break;

        case _LOG_LEVEL_ERROR:
            log_obj.ops.out(LOG_CRLF LOG_LIGHT_RED "[E] %s %s:%d\t%s" LOG_NONE LOG_CRLF LOG_CRLF, current_time, record->func_name, record->line,
                            record->content + offset);
            break;

        case _LOG_LEVEL_WARNING:
            log_obj.ops.out(LOG_CRLF LOG_YELLOW "[W] " LOG_NONE "%s %s:%d\t%s" LOG_CRLF LOG_CRLF, current_time, record->func_name, record->line,
                            record->content + offset);
            break;

        case _LOG_LEVEL_NOTICE:
            log_obj.ops.out(LOG_BLUE "[N] %s %s:%d\t%s" LOG_NONE LOG_CRLF, current_time, record->func_name, record->line, record->content + offset);
            break;

        case _LOG_LEVEL_INFO:
            log_obj.ops.out(LOG_GREEN "[I] " LOG_NONE "%s %s:%d\t%s" LOG_CRLF, current_time, record->func_name, record->line, record->content + offset);
            break;

        case _LOG_LEVEL_DEBUG:
            log_obj.ops.out(LOG_DARY_GRAY "[D] " LOG_NONE "%s %s:%d\t%s" LOG_CRLF, current_time, record->func_name, record->line, record->content + offset);
            break;

        case _LOG_LEVEL_NOISY:
            log_obj.ops.out(LOG_DARY_GRAY "[ ] " LOG_NONE "%s %s:%d\t%s" LOG_CRLF, current_time, record->func_name, record->line, record->content + offset);
            break;

        default: break;
        }
    }
}

void slog_assert_out(const char *file, int line, const char *func, const char *expr)
{
    char current_time[20];
    log_obj.ops.timestamp2string(log_obj.ops.get_timestamp(), current_time, sizeof(current_time));
    log_obj.ops.out(LOG_CRLF LOG_LIGHT_RED "[A] %s %s %s:%d\tAssertion '%s' failed." LOG_NONE LOG_CRLF LOG_CRLF, current_time, file, func, line, expr);
}

/**
 * @brief 日志写缓存函数
 *
 *
 * @param level 日志等级
 * @param is_hexdump 写入的数据是否为十六进制数据
 * @param file 文件名称
 * @param line 行号
 * @param func 函数名
 * @param fmt 自定义消息
 * @param ... 标准不定长参数
 */
void slog_write(slog_level level, bool is_hexdump, char *file_name, int line, char *func_name, const char *format, ...)
{
    slog_record record;

    if (level > log_obj.level)
    {
        return;
    }

    record.level = level;

    record.line         = line;
    record.file_name    = file_name;
    record.func_name    = func_name;
    record.current_time = log_obj.ops.get_timestamp();
    record.is_hexdump   = is_hexdump;

    va_list args;
    va_start(args, format);
    int str_len = vsnprintf(record.content, SLOG_MSG_MAX_LEN, format, args);
    va_end(args);

#if SLOG_USE_ASYNC
    data_buffer tmp;
    tmp.buffer = (uint8_t *)&record;
    tmp.length = sizeof(record);

    // 尝试推送日志到队列
    int       push_result = 0;
    int       retry_count = 0;
    const int max_retries = 5;

    while (1)
    {
#if SLOG_USE_PTHREAD_MUTEX
        log_obj.ops.mutex_lock(&log_obj.mutex);
#endif

        push_result = circular_queue_push(log_obj.queue, &tmp);

#if SLOG_USE_PTHREAD_MUTEX
        log_obj.ops.mutex_unlock(&log_obj.mutex);
#endif

        if (push_result == TASK_QUEUE_SUCCESS)
        {
            break;
        }

        // printf("log queue is full, wait for refresh\n");

        // 队列已满时的处理
        if (++retry_count > max_retries)
        {
            log_obj.ops.mutex_lock(&log_obj.mutex);
            pthread_cond_wait(&space_cond, &log_obj.mutex); // 等待空间可用
            log_obj.ops.mutex_unlock(&log_obj.mutex);
            retry_count = 0;
        }

// 立即触发处理
#if SLOG_USE_PTHREAD_COND
        log_obj.ops.mutex_lock(&log_obj.mutex);
        pthread_cond_signal(&log_cond);
        log_obj.ops.mutex_unlock(&log_obj.mutex);
#endif

        usleep(1000); // 短暂等待
    }

    if (circular_queue_remaining(log_obj.queue) < (SLOG_BUFF_NUM / 4))
    {
        // printf("Log queue space is in a hurry!\n");

        // 当队列剩余空间低于阈值时提前触发
        log_obj.is_refresh = true;
#if SLOG_USE_PTHREAD_COND
        log_obj.ops.mutex_lock(&log_obj.mutex);
        pthread_cond_signal(&log_cond);
        log_obj.ops.mutex_unlock(&log_obj.mutex);
#endif
    }

#else
    char current_time_string[20];
    log_obj.ops.timestamp2string(record.current_time, current_time_string, sizeof(current_time_string));
    _slog_out(&record, current_time_string);
#endif
}

/**
 * @brief 输入内存的16进制数据
 *
 * @param level 日志等级
 * @param data 内存数据地址
 * @param size 内存数据大小
 * @param file 文件名称
 * @param line 行号
 * @param func 函数名
 * @param fmt 自定义消息
 * @param ... 标准不定长参数
 */
void _log_hexdump(slog_level level, const uint8_t *data, size_t size, const char *file, int line, const char *func, const char *fmt, ...)
{
    if (level > log_obj.level)
        return;

    char    header[SLOG_MSG_MAX_LEN] = {0};
    va_list args;

    // 格式化自定义消息
    va_start(args, fmt);
    vsnprintf(header, sizeof(header) - 1, fmt, args);
    va_end(args);

    // 将日志头开头的回车定位到日志真正的开头
    const uint32_t offset = is_new_line_for_head(header, strlen(header));

    if (offset == 0)
    {
        // slog_write(level, true, (char *)file, line, (char *)func, "[HEX]    memory address:%p %s", data, header);
        slog_write(level, true, (char *)file, line, (char *)func, "[HEX] %s:%d %s", func, line, header);
    }
    else
    {
        char new_header[SLOG_MSG_MAX_LEN] = {0};

        for (uint32_t i = 0; i < offset; i++)
        {
            snprintf(new_header + i, sizeof(new_header) - 1, "%c", '\n');
        }

        snprintf(new_header + offset, sizeof(new_header) - 1, "[HEX] %s:%d %s", func, line, header + offset);
        // snprintf(new_header + offset, sizeof(new_header) - 1, "[HEX]    memory address:%p %s", data, header + offset);

        slog_write(level, true, (char *)file, line, (char *)func, new_header);
    }

    // 分块处理二进制数据
    for (size_t i = 0; i < size; i += SLOG_HEX_LINE_MAX)
    {
        char hex_part[3 * SLOG_HEX_LINE_MAX + 1] = {0}; // 存储十六进制部分
        char asc_part[SLOG_HEX_LINE_MAX + 1]     = {0}; // 存储ASCII部分

        size_t line_len = (size - i) > SLOG_HEX_LINE_MAX ? SLOG_HEX_LINE_MAX : (size - i);

        // 单次循环生成双视图
        for (size_t pos = 0; pos < line_len; pos++)
        {
            uint8_t byte = data[i + pos];

            // 填充十六进制部分 (3字节/字符)
            snprintf(hex_part + pos * 3, 4, "%02x ", byte);

            // 填充ASCII部分 (1字节/字符)
            asc_part[pos] = (byte >= 0x20 && byte <= 0x7E) ? byte : '.';
        }

        // 构建完整日志行
        char line_buf[SLOG_MSG_MAX_LEN];
        int  offset = snprintf(line_buf, sizeof(line_buf), "%07lX  %-48s | %s", i, hex_part, asc_part);

        slog_write(level, true, (char *)file, line, (char *)func, "%s", line_buf);
    }
}

#if SLOG_USE_ASYNC
/**
 * @brief 日志输出行为
 *
 * 批量处理优化
 * 每次处理最大批量日志（LOG_BUFF_NUM条）减少锁竞争，提升吞吐量
 */
static void slog_output(void)
{
    const int cur_queuq_size = circular_queue_size(log_obj.queue);

    if (!cur_queuq_size)
        return;

    if (!log_obj.ops.out)
        return;

    uint8_t buffer[SLOG_BUFF_NUM][sizeof(slog_record)];

    data_buffer tmp[SLOG_BUFF_NUM];

#if SLOG_USE_PTHREAD_MUTEX
    log_obj.ops.mutex_lock(&log_obj.mutex);
#endif
    for (int i = 0; i < cur_queuq_size; i++)
    {
        tmp[i].buffer = buffer[i];
        circular_queue_pop(log_obj.queue, &tmp[i]);
    }

#if SLOG_USE_PTHREAD_MUTEX && SLOG_USE_ASYNC
    pthread_cond_broadcast(&space_cond); // 通知可能有空间可用了
    log_obj.ops.mutex_unlock(&log_obj.mutex);
#endif

    for (int i = 0; i < cur_queuq_size; i++)
    {
        slog_record *record = (slog_record *)tmp[i].buffer;
        // printf("%s:%d %s,%s\n", __func__, __LINE__, record->file_name, record->func_name);

        char current_time[20];
        log_obj.ops.timestamp2string(record->current_time, current_time, sizeof(current_time));

        _slog_out(record, current_time);
    }
}

/**
 * @brief 日志线程函数
 * 动态调整超时时间，根据队列负载动态调整等待时间，队列越满唤醒越及时。
 *
 * @param args
 * @return void*
 */
static void *slog_pthread(void *args)
{
    int dynamic_timeout_ms = 100;

    struct timespec ts;
    while (1)
    {
#if SLOG_USE_PTHREAD_COND
        const int cur_queuq_size = circular_queue_size(log_obj.queue);

        if (cur_queuq_size > (SLOG_BUFF_NUM * 3 / 4)) // 队列超过75%时更积极
        {
            dynamic_timeout_ms = 1;
        }
        // else if (cur_queuq_size > 0)
        // {
        //     dynamic_timeout_ms = 50;
        // }
        else
        {
            dynamic_timeout_ms = 100;
        }

        clock_gettime(CLOCK_REALTIME, &ts);
        ts.tv_nsec += (dynamic_timeout_ms % 1000) * 1000 * 1000;
        ts.tv_sec += dynamic_timeout_ms / 1000; // ms ->s

#if SLOG_USE_PTHREAD_MUTEX
        log_obj.ops.mutex_lock(&log_obj.mutex);
#endif
        pthread_cond_timedwait(&log_cond, &log_obj.mutex, &ts);
#if SLOG_USE_PTHREAD_MUTEX
        log_obj.ops.mutex_unlock(&log_obj.mutex);
#endif

#else
        // 传统定时检查
        usleep(10000); // 10ms
#endif

        // 无论是否被唤醒都执行输出
        slog_output();
        log_obj.is_refresh = false;
    }
}
#endif

void set_log_level(slog_level level)
{
    log_obj.level = level;
}

static int default_timestamp2string(int timestamp, char *string, uint16_t size)
{
    // 计算毫秒、秒、分钟、小时
    int hours        = timestamp / 3600000;      // 获取小时
    int minutes      = (timestamp / 60000) % 60; // 获取分钟
    int seconds      = (timestamp / 1000) % 60;  // 获取秒
    int milliseconds = timestamp % 1000;         // 获取毫秒

    // 按照 [HH:MM:SS:MMM] 格式输出
    return snprintf(string, (size < 15) ? size : 15, "[%02d:%02d:%02d:%03d]", hours, minutes, seconds, milliseconds);
}

static uint32_t default_get_timestamp(void)
{
    return 0;
}

/**
 * @brief 日志初始化
 *
 * @param level 日志输出的最低等级
 * @param ops 操作函数集，该成员函数指针若为空，使用默认的函数
 */
int slog_init(slog_level level, struct slog_operate ops)
{

#if SLOG_USE_PTHREAD_MUTEX
    pthread_mutex_init(&log_obj.mutex, NULL);
    if (!ops.mutex_lock)
        ops.mutex_lock = (mutex_lock_func_t)pthread_mutex_lock;
    if (!ops.mutex_unlock)
        ops.mutex_unlock = (mutex_unlock_func_t)pthread_mutex_unlock;
#endif

    if (!ops.out)
        ops.out = printf;
    if (!ops.timestamp2string)
        ops.timestamp2string = default_timestamp2string;
    if (!ops.get_timestamp)
        ops.get_timestamp = default_get_timestamp;

    log_obj.level = level;
    log_obj.ops   = ops;

#if SLOG_USE_ASYNC
    if (circular_queue_init(&log_queue, SLOG_BUFF_NUM, sizeof(slog_record)))
        return -1;
    log_obj.queue = &log_queue;

    pthread_t      thread_id = 0;
    pthread_attr_t attr;

    pthread_attr_init(&attr);
    pthread_attr_setstacksize(&attr, 0x2000);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (pthread_create(&thread_id, &attr, slog_pthread, NULL))
    {
        pthread_attr_destroy(&attr);
        return -3;
    }
#endif
    return 0;
}

#endif